package com.elegant.code.aop.redission;

import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 同步锁 AOP
 * 创建者	张志朋
 * 创建时间	2015年6月3日
 * @transaction 中  order 大小的说明
 * https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/htmlsingle/#transaction-declarative-annotations
 * https://docs.spring.io/spring/docs/4.3.14.RELEASE/javadoc-api/
 */
@Component
@Aspect
@Order(1)//order越小越先执行，先执行的后结束，保证锁的有效性
public class RedissionLockAspect {
	@Autowired
	RedissonClient redisson;
	@Pointcut("@annotation(com.elegant.code.aop.redission.RedissonLock)")
	public void lockAspect() {}
    @Around("lockAspect()")
    public  Object around(ProceedingJoinPoint joinPoint) {
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		Method method = signature.getMethod();
		RedissonLock redissonLock = method.getAnnotation(RedissonLock.class);
		long waitTime = redissonLock.waitTime();
		long leaseTime = redissonLock.leaseTime();
		boolean fair = redissonLock.fair();
		String name = redissonLock.name();
		Object[] args = joinPoint.getArgs();
		//把注解方法参数拼在一起作为锁的资源key
		//TODO 此处根据编码习惯来决定如何约定key
		if (ArrayUtils.isNotEmpty(args))
			for (Object arg : args)
				name = name + ":" + arg;
		RLock lock;
		if (fair) lock = redisson.getFairLock(name);
		else lock = redisson.getLock(name);
		Object obj = null;
		try {
			lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
			obj = joinPoint.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
			throw new RuntimeException();       
		} finally{
			lock.unlock();//手动释放锁
		}
    	return obj;
    } 
}
